package jm.data.structure.tree;

import java.util.Comparator;

/**
 * @Description 二叉搜索树
 * @date 2022/4/9 14:43
 */
@SuppressWarnings({"all"})
public class BinarySearchTree<E> extends BinaryTree<E> {

    private Comparator<E> comparator;

    public BinarySearchTree(Comparator<E> comparator){
        this.comparator = comparator;
    }

    public BinarySearchTree(){
        this(null);
    }

    /**
     * 添加元素
     * @param e
     */
    public void add(E e){
        elementNotNullCheck(e);
        // 添加第一个节点
        if (root == null){
            root = createNode(e,null);
            size++;
            // 添加节点之后的处理
            afterAdd(root);
            return;
        }
        // 记录父节点
        Node<E> parent = root;
        // 记录当前比较的节点
        Node<E> node = root;
        // 记录比较值
        int cmp = 0;
        while (node != null){
            // 获取与当前节点的比较值
            cmp = compare(e, node.e);
            // 保存父节点
            parent = node;
            if (cmp > 0){ // 大于当前节点的值，就找右子节点
                node = node.right;
            }else if (cmp < 0){ // 小于当前节点的值就找左子节点
                node = node.left;
            }else { // 相等
                // 用传入的值覆盖掉原先该节点的值
                node.e = e;
                return;
            }
        }
        // 根据最后一次的比较值判断放在左子节点还是右子节点。
        Node<E> nowNode = createNode(e,parent);
        if (cmp > 0){
            parent.right = nowNode;
        }else {
            parent.left = nowNode;
        }
        // 添加节点之后的处理
        afterAdd(nowNode);
        size++;
    }

    /**
     * 添加节点之后处理，由子类去实现
     * @param node
     */
    protected void afterAdd(Node<E> node){

    }

    /**
     * 删除节点之后的处理，由子类去实现
     * @param node
     * @param replacement
     */
    protected void afterRemove(Node<E> node){

    }

    /**
     * 删除元素节点
     * @param e
     */
    public void remove(E e){
        remove(node(e));
    }

    /**
     * 根据传入的节点执行删除
     * @param node
     */
    private void remove(Node<E> node){
        if (node == null){
            return;
        }
        size--;

        // 判断度为2
        if (node.hasTwoChildren()){
            // 获取后继节点
            Node<E> s = successor(node);
            // 后继节点的值覆盖当前节点的值
            node.e = s.e;
            // 将node指向s节点
            node = s;
        }

        // 获取被删除节点的子节点，如果左子节点为空则获取右子节点；
        // 如果子节点都为空，则表示该节点是叶子节点返回null。
        Node<E> relacement = node.left != null ? node.left : node.right;

        if (relacement != null){ // node 是度为1的节点
            // 更改parent
            relacement.parent = node.parent;
            // 更改parent的left（right）的指向
            if (node.parent == null){ // node是度为1的根节点。
                root = relacement;
            }else if (node == node.parent.left){ // 被删除节点是父节点的左子节点
                node.parent.left = relacement;
            } else { // 被删除节点是父节点的右子节点
                node.parent.right = relacement;
            }
            // 删除之后处理
            afterRemove(relacement);
        }else if(node.parent == null){ // node是叶子节点并且是根节点
            root = null;

            // 删除之后处理
            afterRemove(node);
        }else { //node 是普通叶子节点
            if (node == node.parent.left){ // 当前节点是父节点的右子节点
                node.parent.left = null;
            }else { // 是父节点的左子节点
                node.parent.right = null;
            }

            // 删除之后处理
            afterRemove(node);
        }
    }

    /**
     * 根据值获取节点
     * @param e
     * @return 节点对象
     */
    private Node<E> node(E e){
        Node<E> node = this.root;
        while (node != null){
            int cmp = compare(e,node.e);
            if (cmp == 0){
                return node;
            }
            if (cmp > 0){
                node = node.right;
            }else {
                node = node.left;
            }
        }
        return null;
    }

    /**
     * 判断是否包含元素
     * @param e
     * @return
     */
    public boolean contains(E e) {
        return node(e) != null;
    }

    /**
     * e为空时作为非法参数抛出异常
     * @param e
     */
    private void elementNotNullCheck(E e){
        if (e == null){
            throw new IllegalArgumentException("e must not be null");
        }
    }

    /**
     * 用 e1 - e2 来判断大小
     * @param e1
     * @param e2
     * @return 0：e1和e2相等；大于0：e1大于e2；小于0：e1小于e2。
     */
    private int compare(E e1, E e2){
        if (comparator != null){
            return comparator.compare(e1,e2);
        }
        return ((Comparable<E>)e1).compareTo(e2);
    }

}
